package com.heima.user.service.impl;


import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.heima.common.user.ExcelConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserExcel;
import com.heima.user.listen.DataEasyExcelListener;
import com.heima.user.mapper.ApUserMapper;
import com.heima.user.service.ApExcelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author huan
 * @serial 每天一百行, 致敬未来的自己
 * @Description
 */
@Service
@Slf4j
public class ApExcelServiceImpl implements ApExcelService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired(required = false)
    private ApUserMapper apUserMapper;

    @Override
    public ResponseResult bulkImport(MultipartFile file) {
        int repetitionNumber = 0, successNumber = 0;
        SetOperations<String, String> ops = stringRedisTemplate.opsForSet();
        try {
            InputStream inputStream = file.getInputStream();
            DataEasyExcelListener<ApUserExcel> listener = new DataEasyExcelListener<>();
            //读出excel中数据
            EasyExcel.read(inputStream, ApUserExcel.class, listener).sheet("用户导入模板1").doRead();
            //读出表格中数据
            List<ApUserExcel> info = listener.getData();
            //重复的数据,根据手机号去重
            //List<ApUserExcel> newList = getRepeatList(info);
            List<ApUserExcel> newList = info.stream().collect(Collectors.collectingAndThen(
                    Collectors.toCollection(() -> new TreeSet<>(
                            Comparator.comparing(ApUserExcel::getPhone))), ArrayList::new));
            //info去除重复的数据
            info.removeAll(newList);
            //所有导入的手机号
            List<String> phoneList = newList.stream().map(ApUserExcel::getPhone).collect(Collectors.toList());
            String[] array = phoneList.toArray(new String[phoneList.size()]);
            ops.add(ExcelConstants.IMPORT_PHONE, array);
            //批量插入数据
            //redis中取差集,即数据库手机号不存在的数据
            Set<String> infos = ops.difference(ExcelConstants.IMPORT_PHONE, ExcelConstants.PHONE_INIT);
            ArrayList<String> list = new ArrayList<>();
            list.addAll(infos);

            //获取需要插入数据库的集合
            List<ApUser> apUserList = newList.stream().filter(s -> list.contains(s.getPhone())).map(excel -> {
                ApUser apUser = new ApUser();
                BeanUtils.copyProperties(excel, apUser);
                String pwd = DigestUtils.md5DigestAsHex((apUser.getPassword() + apUser.getSalt()).getBytes());
                apUser.setPassword(pwd);
                apUser.setStatus(false);
                apUser.setFlag((short) 0);
                apUser.setCreatedTime(new Date());
                return apUser;
            }).collect(Collectors.toList());
            //成功数量
            successNumber = apUserList.size();
            //为保证行为  如果excel数据过多  要求每1000条  执行一次 批量insert操作
            List<List<ApUser>> partition = Lists.partition(apUserList, 1000);
            for (List<ApUser> userList : partition) {
                apUserMapper.bulkInsert(userList);
            }
            //手机号已存在,未导入成功的数据,取交集
            Set<String> intersect = ops.intersect(ExcelConstants.IMPORT_PHONE, ExcelConstants.PHONE_INIT);
            //删除redis重复手机号的key,后面会重新插入对象转成字符串的集合
            ops.getOperations().delete(ExcelConstants.IMPORT_PHONE);
            //所有的excel对象过滤出因数据库重复而失败的数量
            List<ApUserExcel> errList = newList.stream().filter(s -> intersect.contains(s.getPhone())).collect(Collectors.toList());
            info.addAll(errList);
            //将对象集合转换成JSON字符串
            List<String> collect = info.stream().map(JSON::toJSONString).collect(Collectors.toList());
            //将失败的写入redis,方便下载获取对应user对象
            String[] objects = collect.toArray(new String[collect.size()]);
            stringRedisTemplate.opsForSet().add(ExcelConstants.REPETITION_PHONE, objects);
            repetitionNumber = info.size();
            //设置重复数据的有效期 1H
            stringRedisTemplate.expire(ExcelConstants.REPETITION_PHONE, 1, TimeUnit.HOURS);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //删除redis中的导入成功手机号
            ops.getOperations().delete(ExcelConstants.IMPORT_PHONE);
        }
        //组装数据返回
        HashMap<String, String> map = new HashMap<>();
        map.put("repetitionNumber", repetitionNumber + "");
        map.put("successNumber", successNumber + "");
        map.put("repetitionId", ExcelConstants.REPETITION_PHONE);
        return ResponseResult.okResult(map);
    }

    /**
     * 传入一个集合,获取手机号相同的集合
     *
     * @param
     * @return
     */
    //public List<ApUserExcel> getRepeatList(List<ApUserExcel> list) {
    //    List<ApUserExcel> newList = new ArrayList();
    //    for (int i = 0; i < list.size(); i++) {
    //        for (int j = i + 1; j < list.size(); j++) {
    //            if (list.get(i).getPhone().equals(list.get(j).getPhone())) {
    //                newList.add(list.get(i));
    //                break;
    //            }
    //        }
    //    }
    //    return newList;
    //}
    @Override
    public void download(HttpServletResponse response, String repetitionId) {

        try {
            //设置响应头
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            String fileName = URLEncoder.encode("未成功导入数据", "utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
            //查询redis获取失败的数据对象 (对象转换为JOSN字符串)
            Set<String> members = stringRedisTemplate.opsForSet().members(repetitionId);
            //将字符串转换为ApUserExcel对象
            List<ApUserExcel> userExcels = members.stream().map(user ->
                    JSON.parseObject(user, ApUserExcel.class)
            ).collect(Collectors.toList());
            //写入数据
            EasyExcel.write(response.getOutputStream(), ApUserExcel.class)
                    .sheet("用户导入模板1").doWrite(userExcels);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
